///PHILOTES Source Code.  (C)2012 PhiloLabs

#include "OgreStableHeaders.h"

#include "OgreMeshSerializer.h"
#include "OgreMeshFileFormat.h"
#include "OgreMesh.h"
#include "OgreSubMesh.h"
#include "core/exception.h"
#include "core/logManager.h"
#include "OgreSkeleton.h"


namespace Ogre {

    const unsigned short HEADER_CHUNK_ID = 0x1000;
    //---------------------------------------------------------------------
    MeshSerializer::MeshSerializer()
		:mListener(0)
    {
		// Init implementations
		// String identifiers have not always been 100% unified with OGRE version
		
		// Note MUST be added in reverse order so latest is first in the list
		mVersionData.push_back(PHILO_NEW MeshVersionData(
			MESH_VERSION_1_8, "[MeshSerializer_v1.8]", 
			PHILO_NEW MeshSerializerImpl()));

		mVersionData.push_back(PHILO_NEW MeshVersionData(
			MESH_VERSION_1_7, "[MeshSerializer_v1.41]", 
			PHILO_NEW MeshSerializerImpl_v1_41()));

		mVersionData.push_back(PHILO_NEW MeshVersionData(
			MESH_VERSION_1_4, "[MeshSerializer_v1.40]", 
			PHILO_NEW MeshSerializerImpl_v1_4()));

		mVersionData.push_back(PHILO_NEW MeshVersionData(
			MESH_VERSION_1_0, "[MeshSerializer_v1.30]", 
			PHILO_NEW MeshSerializerImpl_v1_3()));
		mVersionData.push_back(PHILO_NEW MeshVersionData(
			MESH_VERSION_LEGACY, "[MeshSerializer_v1.20]", 
			PHILO_NEW MeshSerializerImpl_v1_2()));

		mVersionData.push_back(PHILO_NEW MeshVersionData(
			MESH_VERSION_LEGACY, "[MeshSerializer_v1.10]", 
			PHILO_NEW MeshSerializerImpl_v1_1()));
		
    }
    //---------------------------------------------------------------------
    MeshSerializer::~MeshSerializer()
    {
        // delete map
        for (MeshVersionDataList::iterator i = mVersionData.begin();
            i != mVersionData.end(); ++i)
        {
            PHILO_DELETE *i;
        }
        mVersionData.clear();

    }
    //---------------------------------------------------------------------
    void MeshSerializer::exportMesh(const Mesh* pMesh, const String& filename,
		Endian endianMode)
    {
		std::fstream *f = PH_NEW_T(std::fstream, Memory::ObjectHeap)();
		f->open(filename.c_str(), std::ios::binary | std::ios::out);
		DataStreamPtr stream(PHILO_NEW FileStreamDataStream(f));

        exportMesh(pMesh, stream, endianMode);

		stream->close();
    }
    //---------------------------------------------------------------------
    void MeshSerializer::exportMesh(const Mesh* pMesh, const String& filename,
									MeshVersion version, Endian endianMode)
    {
		std::fstream *f = PH_NEW_T(std::fstream, Memory::ObjectHeap)();
		f->open(filename.c_str(), std::ios::binary | std::ios::out);
		DataStreamPtr stream(PHILO_NEW FileStreamDataStream(f));
		
        exportMesh(pMesh, stream, version, endianMode);
		
		stream->close();
    }
	//---------------------------------------------------------------------
	void MeshSerializer::exportMesh(const Mesh* pMesh, DataStreamPtr stream,
		Endian endianMode)
	{
		exportMesh(pMesh, stream, MESH_VERSION_LATEST, endianMode);
	}
	//---------------------------------------------------------------------
	void MeshSerializer::exportMesh(const Mesh* pMesh, DataStreamPtr stream,
									MeshVersion version, Endian endianMode)
	{
		if (version == MESH_VERSION_LEGACY)
			PHILO_EXCEPT(Exception::ERR_INVALIDPARAMS, 
						"You may not supply a legacy version number (pre v1.0) for writing meshes.",
						"MeshSerializer::exportMesh");
		
		MeshSerializerImpl* impl = 0;
		if (version == MESH_VERSION_LATEST)
			impl = mVersionData[0]->impl;
		else 
		{
			for (MeshVersionDataList::iterator i = mVersionData.begin(); 
				 i != mVersionData.end(); ++i)
			{
				if (version == (*i)->version)
				{
					impl = (*i)->impl;
					break;
				}
			}
		}
		
		if (!impl)
			PHILO_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Cannot find serializer implementation for "
					"specified version", "MeshSerializer::exportMesh");

					
		impl->exportMesh(pMesh, stream, endianMode);
	}
    //---------------------------------------------------------------------
    void MeshSerializer::importMesh(DataStreamPtr& stream, Mesh* pDest)
    {
        determineEndianness(stream);

        // Read header and determine the version
        unsigned short headerID;
        
        // Read header ID
        readShorts(stream, &headerID, 1);
        
        if (headerID != HEADER_CHUNK_ID)
        {
            PHILO_EXCEPT(Exception::ERR_INTERNAL_ERROR, "File header not found",
                "MeshSerializer::importMesh");
        }
        // Read version
        String ver = readString(stream);
        // Jump back to start
        stream->seek(0);

        // Find the implementation to use
		MeshSerializerImpl* impl = 0;
		for (MeshVersionDataList::iterator i = mVersionData.begin(); 
			 i != mVersionData.end(); ++i)
		{
			if ((*i)->versionString == ver)
			{
				impl = (*i)->impl;
				break;
			}
		}			
		if (!impl)
            PHILO_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Cannot find serializer implementation for "
						"mesh version " + ver, "MeshSerializer::importMesh");
		
        // Call implementation
        impl->importMesh(stream, pDest, mListener);
        // Warn on old version of mesh
        if (ver != mVersionData[0]->versionString)
        {
			Core::LogManager::Instance()->stream().write(
				"WARNING: %s is an older format (%s); you should upgrade it as soon as possible using the OgreMeshUpgrade tool.",
				pDest->getName().c_str(), ver.c_str() );
        }

    }
    //---------------------------------------------------------------------
	void MeshSerializer::setListener(Ogre::MeshSerializerListener *listener)
	{
		mListener = listener;
	}
	//-------------------------------------------------------------------------
	MeshSerializerListener *MeshSerializer::getListener()
	{
		return mListener;
	}
}

